#include <sstream>
#include "Xps/Object/ObjectFactory.h"
#include "Xps/Message/ACKMessage.h"
#include "Xps/Message/PingMessage.h"
#include "Xps/Message/TextMessage.h"

namespace Xps
{
    ObjectFactory::ObjectFactory(void)
    {
    }

    ObjectFactory::~ObjectFactory(void)
    {
    }

    const IObject::Ptr ObjectFactory::FromBytes(const byte *_Src, const long &_Size) const
    {
        const ObjectType &type = *((const ObjectType *)_Src);

        std::stringstream ss;
        ss.write(_Src + sizeof(ObjectType),_Size - sizeof(ObjectType));
        boost::archive::binary_iarchive ia(ss);

        IObject::Ptr object = ConstructObject(type,ia);

        return object;
    }

    long ObjectFactory::ToBytes( const IObject::Ptr _Object, byte *_Dst, const long &_MaxSize ) const
    {
        ObjectType type = _Object->getType();

        std::stringstream ss;
        boost::archive::binary_oarchive oa(ss);
        oa<<*_Object;
        const std::string buffer = ss.str();
        long length = buffer.size();

        if((sizeof(ObjectType) + length) > _MaxSize)
        {
            throw std::exception("Packet too large!");
        }

        std::copy((byte *)&type, ((byte *)&type)+sizeof(ObjectType), _Dst);
        std::copy(buffer.data(),buffer.data()+length,_Dst);

        return (sizeof(ObjectType) + length);
    }

    const IObject::Ptr ObjectFactory::ConstructObject( const ObjectType &_Type, boost::archive::binary_iarchive &ia ) const
    {
        IObject::Ptr object;

        switch(_Type)
        {
        case PING_MESSAGE:
            {
                object = IObject::Ptr(new PingMessage());
                ia>>*object;
                break;
            }
        case ACK_MESSAGE:
            {
                object = IObject::Ptr(new ACKMessage());
                ia>>*object;
                break;
            }
        case TEXT_MESSAGE:
            {
                object = IObject::Ptr(new TextMessage());
                ia>>*object;
                break;
            }
        default:
            {
                break;
            }
        }
        return object;
    }

    const IObjectFactory * ObjectFactory::getInstance()
    {
        static ObjectFactory object_factory;
        return &object_factory;
    }
};